/*
 * @author Jay Smith (jaysmith024@gmail.com)
 * @version Dec 4, 2008
 * requires -> no requirements or dependencies on any other framework
 */
(function() {
    var Class = function Class(){};
    Class.prototype = {
        preInitialize: function(){
            var retVal = this;
            this.beforeInitialize.apply(this, arguments);
            retVal = (this.initialize) ? this.initialize.apply(this, arguments) : this;
            this.afterInitialize.apply(this, arguments);
            return retVal;
        },
        beforeInitialize : function() {},
        initialize       : function() {return this;},
        afterInitialize  : function() {}
    };
    
    Class.extend = function(destination, source) {
        for (var property in source) { 
            destination[property] = source[property];}
        return destination;
    };
    
    Class.extend(Class, {
		descendents : {},
        
        clone : function() {
            var properties = (arguments[0] || {});
			var self = this;
            var klass = function Class() {
                return (this.preInitialize) ? 
					this.preInitialize.apply(this, arguments) : this;
            };
			this.extend_class(klass);
			this.beforeinherit(klass, properties);
			this.apply_prototype(klass,(properties["@inherits"]||self));
			this.afterinherit(klass, properties);
            return klass;
        },	

        beforeinherit: function(klass, properties){},
        
        afterinherit: function(klass, properties) {
            if (properties) {
                var _traits = (properties["@traits"]||properties.traits||{});
                delete properties.traits;
                delete properties["@traits"];
                
                this.inherit(klass.prototype, properties);
                this.apply_traits(klass, _traits);
			};
			this.descendents[klass.prototype.classname || "anynonmous"] = klass;
            return klass;
        },
		
        extend_class: function(klass) {
         	this.extend(klass, this); 
			klass.descendents = {}; //default 	
        },
		
		apply_prototype: function(klass, parentclass){
            if (parentclass && typeof(parentclass) === "function") {
                var F = function(){}; //optimization
                	F.prototype = parentclass.prototype;
                klass.prototype = new F();
                klass.prototype.constructor = klass;
            }
            return klass;
        },
		
        apply_traits: function(klass, properties){
            var _traits = properties; 
            if (_traits) {
                var traits = [];
                if (_traits.reverse) {
                    traits = traits.concat(_traits.reverse());}
                else {traits.push(_traits);}
				var trait;
                for (var i = 0; (trait = traits[i]); i++) {
                    this.inherit(klass.prototype, trait);}
            }
            return klass;
        },
		
		//adapted from Alex Arnell's inheritance pattern.
        inherit: function(dest, src, fname){
            if (!src || !dest) {return;}
            if (arguments.length === 3) {
                var ancestor = dest[fname], descendent = src[fname], method = descendent;
                descendent = function() {
                    var ref = this.parent;
                    this.parent = ancestor;
                    var result = method.apply(this, arguments);
                    if(ref) {
						this.parent = ref;}
					else {delete this.parent;}
                    return result;
                };
                descendent.valueOf  = function() { return method;};
                descendent.toString = function() { return method.toString();};
                dest[fname] = descendent;
            }
            else {
                for (var prop in src) {
                    if (dest[prop] && typeof(src[prop]) === 'function') { 
                        this.inherit(dest, src, prop);}
                    else { dest[prop] = src[prop]; }
                }
            }
            return dest;
        }
    });

	var JNA = {
		registry  : {},
		    
		namespace : function(ns, anObject) {
			if(JNA.registry[ns] && config.namespaces.frozen[ns]) {
				throw new Error("The namespace named '" + ns + "' is protected -- frozen from being redefined (see config.js). Try a different namespace name, one which is more unique.");
			}
			JNA.allocate_namespace(ns, anObject||{});
			return JNA.registry[ns];
		},
		
		toNamespace:function(aNamespace){
			var scope       = window;
			var nsParts     = aNamespace.split(/\.|\:{2}/); 
			var class_name 	= nsParts.pop();
				
			for (var i = 0; i <= nsParts.length - 1; i++) {
				scope = (scope[nsParts[i]]) ? 
					scope[nsParts[i]] : scope[nsParts[i]] = {};
			}
			return scope; 
		},
			
		allocate_namespace : function(aNamespace, anObject){
			anObject = anObject||{};
			var class_name = aNamespace.split(/\.|\:{2}/).pop();
			anObject.classname = class_name;
			var ns = JNA.toNamespace(aNamespace);
			return (JNA.registry[aNamespace] = ns[class_name] = anObject);
		}
	};
	
	window.NSRegistry = JNA.registry;
	var _klass = Class;
	JNA.namespace("js::Class", function namespace(){
		var args = arguments, ns, obj;
		if(args.length > 1) {
			ns  = args[0];
			obj = args[1];}
		else {
			ns  = null;
			obj = args[0];}
		
		obj.namespace = ns;
        obj.classname = /([a-zA-Z]*)$/.test(ns) ? RegExp.$1:"Anonymous";
        if(typeof obj === "function") {
            if(ns) {JNA.namespace(ns, obj);}}
        else if(typeof obj === "object") {
            var newKlass = _klass;
                newKlass = newKlass.clone(obj);
            if(ns) {JNA.namespace(ns, newKlass);}
        };
        $(window).fireEvent("classloaded", ns);
		return (NSRegistry[ns]||newKlass);
	});
    JNA.namespace("js::namespace",NSRegistry["js::Class"]);
    window.namespace = NSRegistry["js::Class"];
	if(!window.Class) {window.Class = _klass}
	
    
/*    
    JNA.namespace("js::Morph", function Class(){
        var args = arguments, ns, obj;
		if(args.length > 1) {
			ns  = args[0];
			obj = args[1];}
		else {
			ns  = null;
			obj = args[0];}
		
		obj.namespace = ns;
		var newKlass = controls.HtmlElement.clone(obj);
		if(ns) {JNA.namespace(ns, newKlass);}
		return (NSRegistry[ns]||newKlass);
    });
    window.morph = NSRegistry["js::Morph"];
 */  
    
	JNA.namespace("window::instance", function instance(ns, obj) {
        obj.namespace = ns;
        obj.classname = /([a-zA-Z]*)$/.test(ns) ? RegExp.$1 : "Anonymous"; 
        var Klass     = (obj["@inherits"]||_klass);
      	    Klass     = Klass.clone(obj);
        $(window).fireEvent("classloaded", ns);
		return JNA.namespace(ns, new Klass());
	});

    JNA.namespace("js::extend", function extend(destination, source){
        for (var property in source) {
            destination[property] = source[property];}
        return destination;
    });
    
    JNA.namespace("js::merge", function MergeRecursive(obj1, obj2) {
      if(!obj2) return obj1;
      for (var p in obj2) {
        try {
          // Property in destination object set; update its value.
          if ( obj2[p].constructor==Object ) {
            obj1[p] = MergeRecursive(obj1[p], obj2[p]);
          } else {
            obj1[p] = obj2[p];
          }
        } catch(e) {
          // Property in destination object not set; create it and set its value.
          obj1[p] = obj2[p];
    
        }
      }
      return obj1;
    });
})();
